/*Copyright (C) 2014  Yahya Arshad, Email: Yahya.Arshad@Gmail.com

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.android.jpa;


import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


public final class QueryGenerator {

	private Set<Class<?>> tables = new HashSet<Class<?>>();

	public final boolean addClass(Class<?> table) {

		return tables.add(table);
	}

	public final boolean remove(Class<?> table) {

		return tables.remove(table);
	}

	private final String getDataType(String javaType) {

		switch (javaType) {
		case "boolean":
		case "Boolean":
		case "int":
		case "long":
		case "Integer":
		case "Long":
			return "INTEGER";

		case "Float":
		case "float":
		case "double":
		case "Double":

			return "REAL";

		case "String":
		case "char":
		case "Character":
		case "Date":
		case "Calendar":
		case "BigDecimal":
		case "Number":

			return "TEXT";
		default:
			break;
		}
		return null;
	}

	private boolean isColumnNullable(Field field) {

		Column column = field.getAnnotation(Column.class);
		return column == null ? true : column.nullable();
	}

	private String getDefaultColumnValue(Field field) {

		Column column = field.getAnnotation(Column.class);
		return column == null || column.columnDefinition().length() == 0 ? null : column.columnDefinition();
	}

	private String getColumnName(Field field) {

		Column column = field.getAnnotation(Column.class);
		return column == null || column.name().length() == 0 ? field.getName() : column.name();
	}

	public final List<String> createQueries() throws QueryFormationException {

		List<String> queries = new ArrayList<String>();

		java.util.Iterator<Class<?>> iterator = tables.iterator();

		while (iterator.hasNext()) {
			StringBuilder createQuery = new StringBuilder();
			StringBuilder columns = new StringBuilder();
			createQuery.append("CREATE TABLE").append(" ");

			Class<?> clazz = iterator.next();
			Entity table = clazz.getAnnotation(Entity.class);
			String tableName;
			if (null == table) {
				throw new QueryFormationException("@Entity annotation is required: " + clazz.getName());
			}

			tableName = table.name().length() == 0 ? clazz.getSimpleName() : table.name();

			createQuery.append(tableName).append("(");

			int size = clazz.getDeclaredFields().length;
			boolean pkFound = false;

			for (int i = 0; i < size; i++) {

				Field field = clazz.getDeclaredFields()[i];

				if (!isTransient(field)) {

					String columnName = getColumnName(field);

					boolean nullable = isColumnNullable(field);

					String dt = getDataType(field.getType().getSimpleName());

					Id pk = field.getAnnotation(Id.class);

					ForeignKey relation = field.getAnnotation(ForeignKey.class);

					if (null != relation) {
						dt = getDataType(relation.dataType().name());
						nullable = relation.nullable();
					}

					if (null != pk) {
						if (pkFound) {
							throw new QueryFormationException("@Id annotation found on more than one column");
						}
						pkFound = true;
						StringBuilder primaryKeyColumn = new StringBuilder();
						primaryKeyColumn.append(columnName).append(" ");

						primaryKeyColumn.append(dt).append(" ");
						primaryKeyColumn.append("PRIMARY KEY").append(" ");

						GeneratedValue generatedValue = field.getAnnotation(GeneratedValue.class);

						if (null != generatedValue && generatedValue.strategy() != GenerationType.MANUAL) {
							primaryKeyColumn.append("AUTOINCREMENT").append(" ");
						}

						primaryKeyColumn.append("NOT NULL").append(" ");

						columns.insert(0, primaryKeyColumn).append(" ");

					} else if (null != dt) {

						columns.append(columnName).append(" ");

						columns.append(dt).append(" ");
						String defaultColumnValue = getDefaultColumnValue(field);

						if (null != defaultColumnValue) {
							columns.append("DEFAULT").append(" ");
							columns.append(defaultColumnValue).append(" ");
						}
						if (!nullable) {
							columns.append("NOT NULL").append(" ");
						}

					} else {
						System.out.println("Data Type Not Supported: " + field.getType().getSimpleName());
					}

					if (i < size - 1)
						columns.append(", ");

				} else {
					continue;
				}

			}
			if (columns.length() == 0) {
				continue;
			}
			createQuery.append(columns);
			createQuery.append(")");
			queries.add(createQuery.toString());

		}
		return queries;
	}

	private boolean isTransient(Field field) {

		return field.getAnnotation(Transient.class) != null;
	}
}
